API 접근 제어 우회
개요
원래 쿠버 API 서버만이 클러스터에서 다른 것들과 상호작용을 하는 유일한 컴포넌트다.
쿠버네티스는 허브앤스포크 패턴 방식이기 때문에 그렇다.
여기에서 쿠버네티스 감사와 승인 제어를 하게 되는데, 이것을 우회하는 방법이.. 있다!
이걸 잘 제한하는 것도 매우 중요한 사항이다.
정적 파드
kubelet이 자신의 노드에 직접적으로 실행하는 정적 파드는 api 서버로 관리되지 않는다.
쿠버네티스를 통해 정적 파드를 확인할 수 있는 것은 그저 kubelet이 mirror pod를 api에 제공해 보이도록 해주는 것일 뿐이다.
노드의 정적 파드의 양식을 저장하는 디렉토리에 쓰기 권한을 가진 공격자는 멋대로 정적 파드를 만들어버릴 수 있을 것이다.
(근데 그런 케이스가 얼마나..?)
정적 파드는 대부분의 다른 오브젝트에 접근할 수 없다.
그러나 이놈들이 위험한 이유는 hostPath
와 같은 노드의 시스템 레벨의 권한을 획득할 수 있다는 것이다.
그리고 이건 그저 kubelet이 보여주는 것에 불과하다 했는데, 유효하지 않은 네임스페이스를 지정해버리면 그마저도 조회가 안 된다.
진짜? 그런게 만들어진다고?
정적 파드가 승인 제어에 실패하면 kubelet은 api 서버에 등록도 하지 않는데, 그럼에도 사실 파드는 노드에서 돌아간다.
완화책은 다음과 같다.
- 노드에 필요한 경우에만 kubelet이 정적 파드를 만들 수 있도록 설정한다.
- 정적 파드의 경로에 접근할 방법은 제한한다.
- kubelet 설정에 접근할 수도 없게 제한한다.(아니 이건 너무 당연하잖아)
- 관련한 디렉토리에 접근에 대해 정기적으로 감사한다.
kubelet api
kubelet은 노드의 tcp 10250 포트에 http api를 노출한다.
이것은 컨트롤 플레인에서도 당연히 마찬가지인데, 이 api에 대한 직접적인 접근은 노드의 정보를 볼 수 있다.
돌아가는 파드의 정보, 로그, 컨테이너에 대한 명령 실행이 가능해진다.
쿠버네티스에서 노드 오브젝트에 대한 rbac 권한이 있으면 이 api를 쓸 수 있게 되는 것이다.
이건 kubelet 인가에서 자세히 보도록 한다.
https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/#kubelet-authorization
사실 kubelet api에 직접적으로 접근하면 감사나 승인 제어에 걸리지 않는다.
그래서 이 api 주소에 접근할 방법을 찾아낸 공격자는 해당 노드에 대한 정보를 마구 읽을 수 있는 것이다.
이에 대해서 인증 요청을 만들 수 있다.
근데 기본적으로는 익명의 요청을 다 받아들이도록 돼있다.
ㄷㄷ 진짜?
보통 쿠버네티스 공급자는 여기에 웹훅과 인증서 로직을 부여한다.
그렇게 node에 대한 적절한 권한을 가지고 있음을 보장한다.
완화책은 다음과 같다.
- 노드 api에 대해 rbac 권한을 부여한다.
- kubelet 포트에 대해 허용된 ip주소와 포트로만 접근할 수 있게 제한한다.
- 공급자들이 하듯이 kubelet에 인증 로직을 부여한다.
- 인증되지 않은 읽기 전용 포트가 있지는 않은지확인하라
- 이게무슨 말일까
ETCD api
Etcd는 tcp 2379포트를 열고 데이터베이스로서 기능한다.
여기에 접근 가능한 것은 api 서버와, 백업 툴 뿐이다.
여기에 접근하면 진짜 클러스터의 모든 것을 조작할 수 있는 거라 엄청 위험한 것이다.
이 api로의 접근은 클라이언트 인증서 인증을 통해 이뤄진다.
이 인증만 되면 완전 권한을 얻게 된다.
여기에 세밀한 인가 제어가 없어서, 사실 상태 체크만 할 생각으로 만든 인증서라도 사실 모든 권한을 얻는다.
근데 이 api로 또 직접 접근하는 건 아무런 감사에 걸리지 않는다.
이거 털리면 그냥 클러스터는 동물의 숲이 된다!
많은 공급자는 여기에 mtls를 적용한다.
그래서 etcd으로의 접근하는 클라이언트도 인증이 되도록 한다.
완화책은 다음과 같다.
- etcd를 위한 CA가 진짜 해당 서비스만을 위한 인증서임을 보장하라.
- 키 간수 잘해라..(해는 동쪽에서 뜬다고 합니다)
- 네트워크 레벨에서 접근 가능한 ip 범위를 지정하라.
컨테이너 런타임 소켓
컨테이너 런타임에 직접 접근하는 방법도 당연히 api 서버를 우회한다.
Podman 정도만 빼면, 웬만한 모든 컨테이너 런타임이 UNIX 소켓으로 데몬을 노출하고 있다.
kubelet이 런타임에 접근하는 경로도 이것이다.
이 경로를 탈취 당한다는 것은, 한 노드만의 문제가 아닐 수 있다.
해당 노드에 시크릿이 보관되었다면, 이 정보를 탈취할 가능성이 생기며 이를 통해 다른 노드로의 침투도 가능해질 여지가 생긴다.
완화책은 다음과 같다.
- 컨테이너 런타임으로의 접근은 루트만 가능하게 한다.
- 리눅스 커널 네임스페이스 등을 활용해 kubelet을 노드의 다른 컴포넌트로부터 격리한다.
hostPath
로 소켓을 마운팅하지 못하게 막아야 하며, 무조건 노드의 경로는 읽기 전용으로만 마운팅하도록 한다.- 슈퍼유저가 노드에 접근하는 것을 막는다.
관련 문서
이름 | noteType | created |
---|